//=============================================================================
//
//		Copyright (C) 1995,1996 by Paul S. McCarthy and Eric Sunshine.
//				Written by Paul S. McCarthy and Eric Sunshine.
//							All Rights Reserved.
//
//		This notice may not be removed from this source code.
//
//		This object is included in the MiscKit by permission from the authors
//		and its use is governed by the MiscKit license, found in the file
//		"License.rtf" in the MiscKit distribution.	Please refer to that file
//		for a list of all applicable permissions and restrictions.
//
//=============================================================================
//-----------------------------------------------------------------------------
// DirWindow.m
//
//		Manages window which displays directory listing.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// $Id$
// $Log$
//-----------------------------------------------------------------------------
#import "DirWindow.h"
#import "Defaults.h"
#import "NameCache.h"
#import <misckit/MiscTableScroll.h>
#import <misckit/MiscTableCell.h>
#import <errno.h>
#import <appkit/NXImage.h>
#import <appkit/workspaceRequest.h>
#import <assert.h>

#define USE_PRIVATE_ZONE		1

#ifdef _POSIX_SOURCE
# define GET_CURR_DIR(_BF_,_SZ_)		getcwd(_BF_,_SZ_)
#else
# define GET_CURR_DIR(_BF_,_SZ_)		getwd(_BF_)
#endif

#define CASCADE_MAX 10
static int cascadeCounter = 0;
static NXCoord cascadeOriginX;
static NXCoord cascadeOriginY;
static NXCoord cascadeDeltaX;
static NXCoord cascadeDeltaY;

static BOOL defaultAutoSort;
static BOOL defaultShowHidden;
static BOOL defaultHighlightDirs;
static NXColor defaultColor;
static NXSize defaultWinSize;
static Font* defaultFont;
static List* openDirs = 0;
static OwnerCache* owners = 0;
static GroupCache* groups = 0;

static char const COLOR_DEF[]			= "DirColor";
static char const SIZE_DEF[]			= "DirSize";
static char const FONT_DEF[]			= "DirFont";
static char const SORT_DEF[]			= "AutoSort";
static char const HIDDEN_DEF[]			= "ShowHidden";
static char const HLIGHT_DEF[]			= "HighlightDirs";
static char const COL_SIZES_DEF[]		= "ColSizes";
static char const COL_ORDER_DEF[]		= "ColOrder";


//-----------------------------------------------------------------------------
// normalize_path
//-----------------------------------------------------------------------------
static void normalize_path( char *buff, size_t sz )
	{
	char save_dir[ FILENAME_MAX + 1 ];
	char tmp[ FILENAME_MAX + 1 ];

	if (buff != 0 && sz > 0)
		{
		if (GET_CURR_DIR( save_dir, sizeof(save_dir) ) != 0)
			{
			if (chdir( buff ) == 0)
				{
				if (GET_CURR_DIR( tmp, sizeof(tmp) ) != 0)
					{
					int const tmp_len = strlen( tmp );
					if (tmp_len < sz)
						strcpy( buff, tmp );
					else
						{
						strncpy( buff, tmp, sz );
						buff[ sz - 1 ] = '\0';
						}
					}
				chdir( save_dir );
				}
			}
		}
	}


//-----------------------------------------------------------------------------
// str_int
//-----------------------------------------------------------------------------
static char const* str_int( int n )
	{
	static char buff[ 32 ];
	sprintf( buff, "%d", n );
	return buff;
	}


//-----------------------------------------------------------------------------
// str_date
//-----------------------------------------------------------------------------
static char const* str_date( time_t t )
	{
	#define BUFF_SIZE 64
	static char buff[ BUFF_SIZE ];
	strftime( buff, BUFF_SIZE, "%m/%d/%y %H:%M", localtime(&t) );
	return buff;
	}


//-----------------------------------------------------------------------------
// str_perms
//-----------------------------------------------------------------------------
static char const* str_perms( unsigned int mode )
	{
	static char buff[ 16 ];
	char* s;
	int ftype;
	int c;

	s = buff;

	ftype = (mode & S_IFMT);
	switch (ftype)
		{
		case S_IFDIR:	c = 'd'; break;
		case S_IFCHR:	c = 'c'; break;
		case S_IFBLK:	c = 'b'; break;
		case S_IFREG:	c = '-'; break;
		case S_IFLNK:	c = 'l'; break;
		case S_IFSOCK:	c = 's'; break;
		default:		c = '-'; break;
		}
	*s++ = c;
	*s++ = (mode & 0400) ? 'r' : '-';
	*s++ = (mode & 0200) ? 'w' : '-';
	*s++ = (mode & S_ISUID) ? 's' : (mode & 0100) ? 'x' : '-';
	*s++ = (mode & 0040) ? 'r' : '-';
	*s++ = (mode & 0020) ? 'w' : '-';
	*s++ = (mode & S_ISGID) ? 's' : (mode & 0010) ? 'x' : '-';
	*s++ = (mode & 0004) ? 'r' : '-';
	*s++ = (mode & 0002) ? 'w' : '-';
	*s++ = (mode & S_ISVTX) ? 't' : (mode & 0001) ? 'x' : '-';
	*s = '\0';

	return buff;
	}


//-----------------------------------------------------------------------------
// fmt_icon
//-----------------------------------------------------------------------------
static void fmt_icon( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	NXCoord h,w,s;
	NXSize sz;
	NXImage* img;

	w = [ts colSize:ICON_SLOT]; if (w == 0) w = 18;
	h = [ts uniformSizeRows];	if (h == 0) h = 18;
	s = (w < h ? w : h) - 1.0;
	sz.width = s;
	sz.height = s;

	img = [de->dir getImage:de];
	[img setSize: &sz];
	[cell setImage: img];
	}


//-----------------------------------------------------------------------------
// fmt_name
//-----------------------------------------------------------------------------
static void fmt_name( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	[cell setStringValueNoCopy: de->name];
	}


//-----------------------------------------------------------------------------
// fmt_size
//-----------------------------------------------------------------------------
static void fmt_size( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	[[cell setAlignment: NX_RIGHTALIGNED] setIntValue: de->st.st_size ];
	}


//-----------------------------------------------------------------------------
// fmt_modified
//-----------------------------------------------------------------------------
static void fmt_modified( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	int const t = (int) de->st.st_mtime;
	[cell setStringValue: str_date( t )];
	[cell setTag: t];
	}


//-----------------------------------------------------------------------------
// fmt_perms
//-----------------------------------------------------------------------------
static void fmt_perms( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	unsigned int const mode = de->st.st_mode;
	[cell setStringValue: str_perms(mode)];
	[cell setTag: mode];
	}


//-----------------------------------------------------------------------------
// fmt_owner
//-----------------------------------------------------------------------------
static void fmt_owner( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	[cell setStringValueNoCopy: [owners lookup:de->st.st_uid] ];
	}


//-----------------------------------------------------------------------------
// fmt_group
//-----------------------------------------------------------------------------
static void fmt_group( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	[cell setStringValueNoCopy: [groups lookup:de->st.st_gid] ];
	}


//-----------------------------------------------------------------------------
// fmt_hardlinks
//-----------------------------------------------------------------------------
static void fmt_hardlinks( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	[cell setIntValue: de->st.st_nlink];
	}


//-----------------------------------------------------------------------------
// fmt_softlink
//-----------------------------------------------------------------------------
static void fmt_softlink( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	char const* s = de->soft_link;
	[cell setStringValue: s ? s : "" ];
	}


//-----------------------------------------------------------------------------
// fmt_message
//-----------------------------------------------------------------------------
static void fmt_message( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	[cell setStringValue: strerror(de->err) ];
	}


//-----------------------------------------------------------------------------
// fmt_blank
//-----------------------------------------------------------------------------
static void fmt_blank( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	[cell setStringValueNoCopy:""];
	}


//-----------------------------------------------------------------------------
// fmt_unknown
//-----------------------------------------------------------------------------
static void fmt_unknown( MiscTableScroll* ts, DirEntry const* de, id cell )
	{
	[cell setStringValueNoCopy:"?"];
	}



//-----------------------------------------------------------------------------
// FORMAT_FUNC
//-----------------------------------------------------------------------------

typedef void (*FormatFunc)( MiscTableScroll* ts, DirEntry const* de, id cell );

static FormatFunc FORMAT_FUNC[ MAX_SLOT ] =
		{
		fmt_icon,				// ICON_SLOT,
		fmt_name,				// NAME_SLOT,
		fmt_size,				// SIZE_SLOT,
		fmt_modified,			// MODIFIED_SLOT,
		fmt_perms,				// PERMS_SLOT,
		fmt_owner,				// OWNER_SLOT,
		fmt_group,				// GROUP_SLOT,
		fmt_hardlinks,			// HARDLINKS_SLOT,
		fmt_softlink,			// SOFTLINK_SLOT,
		fmt_blank,				// MESSAGE_SLOT
		};

static FormatFunc FORMAT_ERR_FUNC[ MAX_SLOT ] =
		{
		fmt_icon,				// ICON_SLOT,
		fmt_name,				// NAME_SLOT,
		fmt_unknown,			// SIZE_SLOT,
		fmt_unknown,			// MODIFIED_SLOT,
		fmt_unknown,			// PERMS_SLOT,
		fmt_unknown,			// OWNER_SLOT,
		fmt_unknown,			// GROUP_SLOT,
		fmt_unknown,			// HARDLINKS_SLOT,
		fmt_softlink,			// SOFTLINK_SLOT,
		fmt_message,			// MESSAGE_SLOT
		};


//-----------------------------------------------------------------------------
// format_cell
//-----------------------------------------------------------------------------
static inline void format_cell(
		MiscTableScroll* ts,
		DirEntry const* de,
		id cell,
		unsigned int col )
	{
	if (de->err == 0)
		(*(FORMAT_FUNC[col]))( ts, de, cell );
	else
		(*(FORMAT_ERR_FUNC[col]))( ts, de, cell );
	}


//=============================================================================
// IMPLEMENTATION
//=============================================================================
@implementation DirWindow

//-----------------------------------------------------------------------------
// + initCascader
//-----------------------------------------------------------------------------
+ (void) initCascader
	{
	NXSize screenSize;
	[NXApp getScreenSize: &screenSize];
	cascadeOriginX = screenSize.width / 4;
	cascadeOriginY = screenSize.height - 20;
	cascadeDeltaX = 20;
	cascadeDeltaY = 20;
	}


//-----------------------------------------------------------------------------
// + initialize
//-----------------------------------------------------------------------------
+ initialize
	{
	if (self == [DirWindow class])
		{
		[self initCascader];
		openDirs = [[List alloc] init];
		defaultColor = [Defaults getColor:COLOR_DEF fallback:NX_COLORLTGRAY];
		defaultAutoSort = [Defaults getBool:SORT_DEF fallback:NO];
		defaultShowHidden = [Defaults getBool:HIDDEN_DEF fallback:NO];
		defaultHighlightDirs = [Defaults getBool:HLIGHT_DEF fallback:NO];
		owners = [OwnerCache commonInstance];
		groups = [GroupCache commonInstance];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - cascade
//-----------------------------------------------------------------------------
- (void) cascade
	{
	NXCoord top,left;
	left = cascadeOriginX + (cascadeDeltaX * cascadeCounter);
	top	 = cascadeOriginY - (cascadeDeltaY * cascadeCounter);
	[window moveTopLeftTo: left : top ];
	if (++cascadeCounter >= CASCADE_MAX)
		cascadeCounter = 0;
	}


//-----------------------------------------------------------------------------
// - updateButtons
//-----------------------------------------------------------------------------
- (void) updateButtons
	{
	BOOL enable = NO;
	if ([scroll numSelectedRows] == 1)
		{
		MiscCoord_P const row = [scroll selectedRow];
		enable = [dir_array isDirAt:row];
		}
	if (enable != [cdButton isEnabled])
		[cdButton setEnabled: enable];
	}


//-----------------------------------------------------------------------------
// - tableScroll:cellAt::
//-----------------------------------------------------------------------------
- tableScroll:(MiscTableScroll*)ts cellAt: (int)row : (int)col
	{
	id cell = lazy_row[col];
	DirEntry const* const de = [dir_array entryAt:row];
	format_cell( ts, de, cell, col );
	if ([cell respondsTo:@selector(setBackgroundColor:)])
		{
		if (highlightDirs && [dir_array isDir:de])
			[cell setBackgroundColor:NX_COLORCYAN];
		else
			[cell setBackgroundColor:[ts backgroundColor]];
		}
	return cell;
	}


//-----------------------------------------------------------------------------
// - tableScroll:stringValueAt::
//-----------------------------------------------------------------------------
- (char const*) tableScroll:(MiscTableScroll*)ts stringValueAt:(int)r :(int)c
	{
	char const* rc = 0;
	if (r < [dir_array count])
		{
		DirEntry const* const de = [dir_array entryAt:r];
		if (de->err == 0)
			{
			switch (c)
				{
			case SIZE_SLOT:		rc = str_int(de->st.st_size);			break;
			case MODIFIED_SLOT: rc = str_date(de->st.st_mtime);			break;
			case PERMS_SLOT:	rc = str_perms(de->st.st_mode);			break;
			case OWNER_SLOT:	rc = [owners lookup:de->st.st_uid];		break;
			case GROUP_SLOT:	rc = [groups lookup:de->st.st_gid];		break;
			case HARDLINKS_SLOT:rc = str_int(de->st.st_nlink);			break;
			case NAME_SLOT:		rc = de->name;							break;
			case SOFTLINK_SLOT: rc = de->soft_link;						break;
			case MESSAGE_SLOT:
			case ICON_SLOT:
			default:			rc = "";								break;
				}
			}
		else
			{
			switch (c)
				{
			case NAME_SLOT:		rc = de->name;							break;
			case SOFTLINK_SLOT: rc = de->soft_link;						break;
			case MESSAGE_SLOT:	rc = strerror(de->err);					break;
			case SIZE_SLOT:
			case MODIFIED_SLOT:
			case PERMS_SLOT:
			case OWNER_SLOT:
			case GROUP_SLOT:
			case HARDLINKS_SLOT:
			case ICON_SLOT:
			default:			rc = "?";								break;
				}
			}
		}
	return (rc ? rc : "");
	}


//-----------------------------------------------------------------------------
// -intValueAt::
//-----------------------------------------------------------------------------
- (int) intValueAt:(int)r :(int)c
	{
	int rc = 0;
	if (r < [dir_array count])
		{
		DirEntry const* const de = [dir_array entryAt:r];
		if (de->err == 0)
			{
			switch (c)
				{
			case SIZE_SLOT:				rc = de->st.st_size;	break;
			case MODIFIED_SLOT:			rc = de->st.st_mtime;	break;
			case PERMS_SLOT:			rc = de->st.st_mode;	break;
			case OWNER_SLOT:			rc = de->st.st_uid;		break;
			case GROUP_SLOT:			rc = de->st.st_gid;		break;
			case HARDLINKS_SLOT:		rc = de->st.st_nlink;	break;
			case ICON_SLOT:
			case NAME_SLOT:
			case SOFTLINK_SLOT:
			case MESSAGE_SLOT:
			default:											break;
				}
			}
		}
	return rc;
	}

//-----------------------------------------------------------------------------
// - tableScroll:intValueAt::
//-----------------------------------------------------------------------------
- (int) tableScroll:(MiscTableScroll*)ts intValueAt:(int)r :(int)c
	{
	return [self intValueAt:r:c];
	}


//-----------------------------------------------------------------------------
// - tableScroll:tagAt::
//-----------------------------------------------------------------------------
- (int) tableScroll:(MiscTableScroll*)ts tagAt:(int)r :(int)c
	{
	return [self intValueAt:r:c];
	}


//-----------------------------------------------------------------------------
// - tableScroll:fontChangedFrom:to:
//-----------------------------------------------------------------------------
- tableScroll:(MiscTableScroll*)ts
		fontChangedFrom:(Font*)oldFont
		to:(Font*)newFont
	{
	int col;
	for (col = 0; col < MAX_SLOT; col++)
		[lazy_row[col] setFont:newFont];

	defaultFont = newFont;
	[Defaults set:FONT_DEF font:defaultFont];
	return self;
	}


//-----------------------------------------------------------------------------
// - tableScroll:border:slotResized:
//-----------------------------------------------------------------------------
- tableScroll:(MiscTableScroll*)ts
		border:(MiscBorderType)b
		slotResized:(int)n
	{
	char* s;
	assert( b == MISC_COL_BORDER );
	s = [ts colSizesAsString:0 size:0 canExpand:YES];
	[Defaults set:COL_SIZES_DEF str:s];
	free( s );
	return self;
	}


//-----------------------------------------------------------------------------
// - tableScroll:border:slotDraggedFrom:to:
//-----------------------------------------------------------------------------
- tableScroll:(MiscTableScroll*)ts
		border:(MiscBorderType)b
		slotDraggedFrom:(int)fromPos
		to:(int)toPos;
	{
	char* s;
	assert( b == MISC_COL_BORDER );
	s = [ts colOrderAsString:0 size:0 canExpand:YES];
	[Defaults set:COL_ORDER_DEF str:s];
	free( s );
	return self;
	}


//-----------------------------------------------------------------------------
// - fillScroll
//-----------------------------------------------------------------------------
- (void) fillScroll
	{
	char buff[ 128 ];

	[window disableDisplay];

	[scroll renewRows: [dir_array count]];

	if ([scroll autoSortRows])
		[scroll sortRows];

	sprintf( buff, "%d files   %d bytes",
				[dir_array count], [dir_array totalBytes] );
	[countField setStringValue:buff];

	[self updateButtons];
	[[window reenableDisplay] display];
	}


//-----------------------------------------------------------------------------
// - setPath:
//-----------------------------------------------------------------------------
- (void) setPath:(char const*)dirname
	{
	NXZone* const z = [self zone];
	int dirlen;
	if (path != 0) NXZoneFree( z, path );
	if (dirname == 0) dirname = NXHomeDirectory();
	if (dirname == 0) dirname = "/";
	dirlen = strlen( dirname ) + 1;
	path = (char*) NXZoneMalloc( z, dirlen );
	memcpy( path, dirname, dirlen );
	[window setTitle: path];
	}


//-----------------------------------------------------------------------------
// - loadDirectory:
//-----------------------------------------------------------------------------
- (void) loadDirectory:(char const*)dirname
	{
	int rc = [dir_array loadPath:dirname showHidden:showHidden];
	if (rc != 0)
		{
		NXRunAlertPanel( "Can't Open",
				"Cannot open directory, %s\n%d:%s", "OK",0,0,
				path, rc, strerror(rc) );
		}
	}


//-----------------------------------------------------------------------------
// - reload
//-----------------------------------------------------------------------------
- (void) reload
	{
	[self loadDirectory:path];
	[self fillScroll];
	}


//-----------------------------------------------------------------------------
// - load:
//-----------------------------------------------------------------------------
- (void) load:(char const*)dirname
	{
	[self setPath:dirname];
	[self reload];
	}


//-----------------------------------------------------------------------------
// - open:
//-----------------------------------------------------------------------------
- open:sender
	{
	if ([scroll hasRowSelection])
		{
		int i;
		int path_len;
		char buff[ FILENAME_MAX + 1 ];
		MiscIntList* list = [[MiscIntList allocFromZone:[self zone]] init];

		strcpy( buff, path );
		path_len = strlen( buff );
		if (path_len == 0 || buff[path_len - 1] != '/')
			{
			buff[ path_len++ ] = '/';
			buff[ path_len ] = '\0';
			}
			
		[scroll selectedRows: list];
		for (i = [list count]; i-- > 0; )
			{
			int row = [list intAt:i];
			DirEntry const* de = [dir_array entryAt:row];

			strcpy( buff + path_len, de->name );
			if ([dir_array isDir:de])
				[[self class] launchDir: buff];
			else
				[[Application workspace] openFile: buff];
			}
		[list free];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - destroy:
//-----------------------------------------------------------------------------
- destroy:sender
	{
	if ([scroll hasRowSelection])
		{
		size_t total_len = 0;
		char* file_list;
		int i;
		NXZone* const z = [self zone];
		MiscIntList* list = [[MiscIntList allocFromZone:z] init];

		[scroll selectedRows: list];

		for (i = [list count]; i-- > 0; )
			{
			DirEntry const* de = [dir_array entryAt:[list intAt:i]];
			if (de->name != 0)
				total_len += strlen( de->name ) + 1;
			}

		file_list = (char*) NXZoneMalloc( z, total_len + 1 );
		
		total_len = 0;
		for (i = [list count]; i-- > 0; )
			{
			DirEntry const* de = [dir_array entryAt:[list intAt:i]];
			char const* const s = de->name;
			if (s != 0)
				{
				int const len = strlen( s );
				strcpy( file_list + total_len, s );
				total_len += len;
				file_list[ total_len++ ] = '\t';
				}
			}
		file_list[ total_len ] = '\0';

		[[Application workspace]
				performFileOperation: WSM_DESTROY_OPERATION
						source: path
						destination: ""
						files: file_list
						options: ""];

		NXZoneFree( z, file_list );
		[list free];
		[self reload];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - updatePressed:
//-----------------------------------------------------------------------------
- updatePressed:sender
	{
	[self reload];
	return self;
	}


//-----------------------------------------------------------------------------
// - cdPressed:
//-----------------------------------------------------------------------------
- cdPressed:sender
	{
	if ([scroll numSelectedRows] == 1)
		{
		DirEntry const* const de = [dir_array entryAt:[scroll selectedRow]];
		if ([dir_array isDir:de])
			{
			char buff[ FILENAME_MAX * 2 + 1 ];
			strcat( strcat( strcpy( buff, path ), "/" ), de->name );
			normalize_path( buff, sizeof(buff) );
			[self load:buff];
			}
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - autoSortClick:
//-----------------------------------------------------------------------------
- autoSortClick:sender
	{
	BOOL const switchState = [autoSortSwitch state];
	if (autoSort != switchState)
		{
		defaultAutoSort = autoSort = switchState;
		[Defaults set:SORT_DEF bool:defaultAutoSort];
		[scroll setAutoSortRows:switchState];
		if (switchState)
			[scroll sortRows];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - hiddenFilesClick:
//
//		FIXME: Should not have to rescan the whole directory just to show/hide
//		hidden files.  A more careful implementation would not be this sloppy.
//-----------------------------------------------------------------------------
- hiddenFilesClick:sender
	{
	BOOL const switchState = [hiddenFilesSwitch state];
	if (showHidden != switchState)
		{
		defaultShowHidden = showHidden = switchState;
		[Defaults set:HIDDEN_DEF bool:defaultShowHidden];
		[self reload];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - highlightClick:
//-----------------------------------------------------------------------------
- highlightClick:sender
	{
	BOOL const switchState = [highlightSwitch state];
	if (highlightDirs != switchState)
		{
		defaultHighlightDirs = highlightDirs = switchState;
		[Defaults set:HLIGHT_DEF bool:defaultHighlightDirs];
		[scroll display];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - didClick:
//-----------------------------------------------------------------------------
- didClick:sender
	{
	[self updateButtons];
	return self;
	}


//-----------------------------------------------------------------------------
// - didDoubleClick:
//-----------------------------------------------------------------------------
- didDoubleClick:sender
	{
	[self open:sender];
	return self;
	}


//-----------------------------------------------------------------------------
// - makeKeyAndOrderFront:
//-----------------------------------------------------------------------------
- makeKeyAndOrderFront:sender
	{
	[window makeKeyAndOrderFront:sender];
	return self;
	}


//-----------------------------------------------------------------------------
// - windowWillClose:
//-----------------------------------------------------------------------------
- windowWillClose:sender
	{
	[openDirs removeObject:self];
	[NXApp delayedFree: self];
	return self;
	}


//-----------------------------------------------------------------------------
// - windowDidResize:
//-----------------------------------------------------------------------------
- windowDidResize:sender
	{
	NXRect r; [sender getFrame:&r];
	if (r.size.width  != defaultWinSize.width ||
		r.size.height != defaultWinSize.height)
		{
		defaultWinSize = r.size;
		[Defaults set:SIZE_DEF size:defaultWinSize];
		}
	return self;
	}


//-----------------------------------------------------------------------------
// - setDefaultColor
//-----------------------------------------------------------------------------
- (void) setDefaultColor: (NXColor)c
	{
	defaultColor = c;
	[Defaults set:COLOR_DEF color:c];
	}


//-----------------------------------------------------------------------------
// - setDaColors:
//-----------------------------------------------------------------------------
- (void) setDaColors: (NXColor)c
	{
	[window disableDisplay];
	[window setBackgroundColor:c];
	[scroll setColor:c];
	[window reenableDisplay];
	}


//-----------------------------------------------------------------------------
// - draggingEntered:
//-----------------------------------------------------------------------------
- (NXDragOperation) draggingEntered:(id<NXDraggingInfo>)sender
	{
	return ([sender draggingSourceOperationMask] & NX_DragOperationGeneric);
	}


//-----------------------------------------------------------------------------
// - performDragOperation:
//-----------------------------------------------------------------------------
- (BOOL) performDragOperation:(id<NXDraggingInfo>)sender
	{
	[self setDefaultColor:
				NXReadColorFromPasteboard( [sender draggingPasteboard] )];
	[self setDaColors: defaultColor];
	[window display];
	return YES;
	}


//-----------------------------------------------------------------------------
// - initLazyRow
//-----------------------------------------------------------------------------
- (void) initLazyRow
	{
	Font* font = [scroll font];
	NXZone* const z = [self zone];
	int i;
	for (i = 0; i < MAX_SLOT; i++)
		{
		id cell = [[scroll colCellPrototype:i] copyFromZone:z];
		if ([cell respondsTo:@selector(setFont:)])
			[cell setFont:font];
		lazy_row[i] = cell;
		}
	}


//-----------------------------------------------------------------------------
// - initDefaults
//-----------------------------------------------------------------------------
- (void) initDefaults
	{
	static BOOL initialized = NO;
	if (!initialized)
		{
		NXRect r; [window getFrame:&r];
		defaultWinSize = r.size;

		defaultFont = [Defaults getFont:FONT_DEF fallback:[scroll font]];
	
		initialized = YES;
		}
	}


//-----------------------------------------------------------------------------
// - loadDefaults
//-----------------------------------------------------------------------------
- (void) loadDefaults
	{
	NXRect r;
	char const* s;

	[window getFrame:&r];
	r.size = [Defaults getSize:SIZE_DEF fallback:defaultWinSize];
	[window placeWindow:&r];

	autoSort = defaultAutoSort;
	showHidden = defaultShowHidden;
	highlightDirs = defaultHighlightDirs;

	[autoSortSwitch setState:autoSort];
	[hiddenFilesSwitch setState:showHidden];
	[highlightSwitch setState:highlightDirs];

	[scroll setAutoSortRows:autoSort];
	[scroll setFont:defaultFont];
	[self setDaColors:defaultColor];

	s = [Defaults getStr:COL_SIZES_DEF fallback:0];
	if (s)
		[scroll setColSizesFromString:s];

	s = [Defaults getStr:COL_ORDER_DEF fallback:0];
	if (s)
		[scroll setColOrderFromString:s];
	}


//-----------------------------------------------------------------------------
// - initDir:
//-----------------------------------------------------------------------------
- initDir:(char const*)dirname
	{
	char buff[ FILENAME_MAX + 1 ];
	NXZone* const z = [self zone];

	[super init];
	[[NXBundle bundleForClass:[self class]]
		getPath:buff forResource:[[self class] name] ofType:"nib"];
	[NXApp loadNibFile:buff owner:self withNames:NO fromZone:z];
	[window registerForDraggedTypes:&NXColorPboardType count:1];
	path = 0;
	[scroll setDoubleAction: @selector(didDoubleClick:)];
	dir_array = [[DirArray allocFromZone:z] init];
	[self initDefaults];
	[self loadDefaults];
	[self initLazyRow];
	[self load:dirname];
	[openDirs addObject:self];
	[self cascade];
	[window makeKeyAndOrderFront:self];
	return self;
	}


//-----------------------------------------------------------------------------
// - init
//-----------------------------------------------------------------------------
- init
	{
	return [self initDir: NXHomeDirectory()];
	}


//-----------------------------------------------------------------------------
// - free
//-----------------------------------------------------------------------------
- free
	{
	int i;
#if(USE_PRIVATE_ZONE)
	NXZone* const z = [self zone];
#endif
	[window close];
	[window free];
	if (path != 0)
		free( path );
	[dir_array free];
	for (i = 0; i < MAX_SLOT; i++)
		[lazy_row[i] free];
	[super free];
#if(USE_PRIVATE_ZONE)
	NXDestroyZone( z );
#endif
	return 0;
	}


//-----------------------------------------------------------------------------
// - path
//-----------------------------------------------------------------------------
- (char const*) path
	{
	return path;
	}


//-----------------------------------------------------------------------------
// + findDir:
//-----------------------------------------------------------------------------
+ (DirWindow*) findDir:(char const*) normalized_path
	{
	DirWindow* p = 0;
	if (normalized_path != 0)
		{
		char const* s;
		unsigned int i;
		unsigned int const lim = [openDirs count];
		for (i = 0;	 i < lim;  i++)
			{
			p = (DirWindow*) [openDirs objectAt:i];
			s = [p path];
			if (s != 0 && strcmp( s, normalized_path ) == 0)
				return p;
			}
		}
	return 0;
	}


//-----------------------------------------------------------------------------
// + launchDir:
//-----------------------------------------------------------------------------
+ launchDir: (char const*) dirname
	{
	DirWindow* p = 0;
	char buff[ FILENAME_MAX + 1 ];
	if (dirname == 0) dirname = NXHomeDirectory();
	if (dirname == 0) dirname = "/";
	strncpy( buff, dirname, sizeof(buff) );
	buff[ sizeof(buff) - 1 ] = '\0';
	normalize_path( buff, sizeof(buff) );
	if ((p = [self findDir:buff]) != 0)
		[p makeKeyAndOrderFront:self];
	else
		{
#if(USE_PRIVATE_ZONE)
		NXZone* z = NXCreateZone( vm_page_size, vm_page_size, YES );
		p = [[self allocFromZone:z] initDir:buff];
#else
		p = [[self alloc] initDir:buff];
#endif
		}
	return p;
	}


@end
